//
//  WCServiceDelegate.swift
//  RemoteService
//
//  Created by Longxiang Guo (QXQ4633) on 2021/5/24.
//

import Foundation
import Combine
import WatchConnectivity

public protocol WCSessionWrapperProtocol {
    var reachable: Bool { get }
    var delegate: WCSessionWrapperDelegate? { get set }

    func activateSession()
    func sendMessage(
        _ message: [String: Any],
        replyHandler: (([String: Any]) -> Void)?,
        errorHandler: ((Error) -> Void)?
    )
    func transferUserInfo(_ userInfo: [String: Any]) -> WCSessionUserInfoTransfer?
    func transferFile(_ file: URL, metadata: [String: Any]?) -> WCSessionFileTransfer?
    func updateApplicationContext(_ applicationContext: [String: Any]) throws
}

public protocol WCSessionWrapperDelegate: class {
    func session(_ session: WCSessionWrapperProtocol, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?)
    func session(_ session: WCSessionWrapperProtocol, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void)
    func session(_ session: WCSessionWrapperProtocol, didReceiveMessage message: [String: Any])
    func session(_ session: WCSessionWrapperProtocol, didReceiveApplicationContext applicationContext: [String: Any])
    func session(_ session: WCSessionWrapperProtocol, didReceiveUserInfo userInfo: [String: Any])
    func session(_ session: WCSessionWrapperProtocol, didReceiveFile file: WCSessionFile)
    func sessionReachabilityDidChange(_ session: WCSessionWrapperProtocol)
}

class WCSessionWrapper: NSObject, WCSessionWrapperProtocol {
    fileprivate let session: WCSession? = WCSession.isSupported() ? WCSession.default : nil
    var reachable: Bool {
        return session?.isReachable ?? false
    }
    
    var delegate: WCSessionWrapperDelegate?
    
    func activateSession() {
        session?.activate()
        session?.delegate = self
    }
    
    func sendMessage(_ message: [String : Any], replyHandler: (([String : Any]) -> Void)?, errorHandler: ((Error) -> Void)?) {
        session?.sendMessage(message,
                             replyHandler:replyHandler,
                             errorHandler: errorHandler)
    }
    
    func transferUserInfo(_ userInfo: [String : Any]) -> WCSessionUserInfoTransfer? {
       return session?.transferUserInfo(userInfo)
    }
    
    func transferFile(_ file: URL, metadata: [String : Any]?) -> WCSessionFileTransfer? {
        return session?.transferFile(file, metadata: metadata)
    }
    
    func updateApplicationContext(_ applicationContext: [String : Any]) throws {
        try session?.updateApplicationContext(applicationContext)
    }
}


extension WCSessionWrapper: WCSessionDelegate {
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        self.delegate?.session(self, activationDidCompleteWith: activationState, error: error)
    }
    /// When reachable, present the wachKit extension and iOS app is active an running, is ready for send message now
    /// When you prepare to send message to the other side, check it firstly
    func sessionReachabilityDidChange(_ session: WCSession){
        delegate?.sessionReachabilityDidChange(self)
    }
   
 
    func session(_ session: WCSession, didReceiveMessage message: [String : Any]){
        delegate?.session(self, didReceiveMessage: message)
    }

    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void){
       self.session(session, didReceiveMessage: message)
       replyHandler(["State": WCCommand.State.success])
    }

   
     func session(_ session: WCSession, didReceiveMessageData messageData: Data){
        guard let message = try? JSONSerialization.jsonObject(with: messageData, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any] else {
            return
        }
        self.session(session, didReceiveMessage: message)
     }

     func session(_ session: WCSession, didReceiveMessageData messageData: Data, replyHandler: @escaping (Data) -> Void){
        self.session(session, didReceiveMessageData: messageData)
        guard let replyData = try? JSONSerialization.data(withJSONObject: ["State": WCCommand.State.success], options: .prettyPrinted) else {
            return
        }
        replyHandler(replyData)
     }
    
    /** -------------------------- Background Transfers ------------------------- */
     func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]){
        self.session(session, didReceiveMessage: applicationContext)
     }

    @available(iOS 9.0, *)
     func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?){
        #if os(iOS)
        if userInfoTransfer.isCurrentComplicationInfo {
             
        }
        #endif
     }

           
     func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]){
        #if os(watchOS)
        if (userInfo["isCurrentComplicationInfo"] as? Bool) {
            let server = CLKComplicationServer.sharedInstance()
            if let complications = server.activeComplications {
                for complication in complications {
                    // Call this method sparingly. If your existing complication data is still valid,
                    // consider calling the extendTimeline(for:) method instead.
                    server.reloadTimeline(for: complication)
                }
            }
        }
        #endif
        delegate?.session(self, didReceiveUserInfo: userInfo)
     }
  
     func session(_ session: WCSession, didFinish fileTransfer: WCSessionFileTransfer, error: Error?){
        print("finished file transfer - error: \(String(describing: error))")
     }

     func session(_ session: WCSession, didReceive file: WCSessionFile){
    
        delegate?.session(self, didReceiveFile: file)
     }
    
    // MARK: -  For iOS ony, monitor the session state for watch changed
    #if os(iOS)
    ///For toggle watch: **clean up data structures and close files related to outgoing content.**
    func sessionDidBecomeInactive(_ session: WCSession) {
        
    }
    /// When  prepare to subscribe other side, check if the state is activation or not
    func sessionWatchStateDidChange(_ session: WCSession) {
        
    }
    ///For toggle watch: **disable previous watch communication, actcivate new communication**
    func sessionDidDeactivate(_ session: WCSession) {
       // WCSession.default.activate()
    }
    #endif
}
